<?php
namespace Shell;
/**
 * $args_rule like below:
 *   array( 'config' => 'c|con|configure;stdin;boolean' );
 *   Ex: 
 *     ./some_programme.php -config=/etc/my.conf
 *     ./some_programme.php -c/etc/my.conf
 *     ./some_programme.php --configure=/etc/my.conf
 *     ./some_programme.php -c=/etc/my.conf
 *   Return:
 *     array('config' => '/etc/my.conf')
 * @param array $args_rule
 * @param string $end_first_all，可选项有:"end","first","all"。当有重复时，默认"end"取最后一个，"all"将返回一个数组，"first"将返回第一个
 * @return array()
 */
function arguments(array $arguments, array $rules, $end_first_all='end'){
	unset($arguments[0]);
	#$args = implode(' -',$arguments).' -';
	$args = implode(' ',$arguments).' -';
    $use_stdin     = array();
    $is_booleans   = array();
    $not_booleans  = array();
    $all_rules     = array();
	foreach($rules as $key=>$rule){
        $rule_items = explode(';',strtolower($rule));
        if(($index = array_search('stdin',$rule_items))!==false){
            unset($rule_items[$index]); 
            $use_stdin[$key] = true;
        }
        if(($index = array_search('boolean',$rule_items))!==false){
            unset($rule_items[$index]); 
            $is_booleans[$key] = true;
        }else{
            $not_booleans[$key] = true;
        }
        $all_rules[$key] = explode('|',current($rule_items));

        $rule = implode(';',$rule_items);
		#$args_rule[$key] = '/(?:\-+)(?:'.$rule.')((?:(?! -).+)?) -/U';
    }

    $args_rule = array();
    foreach($all_rules as $key=>$rule){
        $rule_str = '';
        if(isset($is_booleans[$key])){
            # 设 xml => x 为本次 boolean，另外还有一个 d 的 boolena ，一个 s 的非 boolean, 那么他的表达式为：
            #    xml |  (?:d|s){2}x    |   x(?:d|s){2}  |  (?:d|s)x  | x(?:d|s)  | x
            $match_items       = array();
            $boolean_items     = array();
            $self_items        = $rule;
            if(count($rule)>1){
                $first_item = array_shift($self_items);
                $end_item = end($rule);
            }else{
                $first_item = '';
                $end_item = $rule[0];
            }

            foreach(array_keys($is_booleans) as $key2){
                if($key2==$key) continue;
                $rule2 = $all_rules[$key2];
                if(count($rule2)>1){ # 只需要缩写
                    array_shift($rule2);
                }
                $boolean_items = array_merge($boolean_items,$rule2);
            }
            
            #echo " BOOLEAN ";
            $match_times = count($boolean_items)+1;
            do{
                $not_boolean_items = array_keys($not_booleans);
                while(count($not_boolean_items)){
                    $all_items   = $boolean_items;
                    $all_items[] = end($all_rules[array_shift($not_boolean_items)]);

                    usort($all_items,
                        function($a,$b){ # 按字符串长度从大到小排列
                            $al = strlen($a);
                            $bl = strlen($b);
                            if($al==$bl) return 0;
                            return ($al>$bl)?-1:1;
                        }
                    );
                    foreach($self_items as $item){
                        $match_items[] = '(?:'.implode('|',$all_items).'){'.$match_times.'}'.$item;  # (?:d|s){2}x
                        $match_items[] = $item.'(?:'.implode('|',$all_items).'){'.$match_times.'}';  # x(?:d|s){2}
                    }
                }
            }while(--$match_times>0);

            $match_items[] = $end_item;
            if($first_item)array_unshift($match_items,$first_item);
            $rule_str = implode('|',$match_items);
        }else{
            # 设 system => s 为本次非 boolean， 另还有两个 boolean x & d ，那么他的表达式为：
            #   system  |  (?:d|x){2}s   |  s(?:d|x){2}   |  (?:d|x)s   |  s(?:d|x) |  s 
            $match_items   = array();
            $boolean_items = array();
            $self_items    = $rule;
            if(count($rule)>1){
                $first_item = array_shift($self_items);
                $end_item = end($rule);
            }else{
                $first_item = '';
                $end_item = $rule[0];
            }

            foreach(array_keys($is_booleans) as $key2){
                $rule2 = $all_rules[$key2];
                if(count($rule2)>1){ # 只需要缩写
                    array_shift($rule2);
                }
                $boolean_items = array_merge($boolean_items,$rule2);
            }

            $match_times = count($boolean_items);
            #echo " NO BOOLEAN ";
            do{
                foreach($self_items as $item){
                    $match_items[] = '(?:'.implode('|',$boolean_items).'){'.$match_times.'}'.$item;  # (?:d|x){2}s
                    $match_items[] = $item.'(?:'.implode('|',$boolean_items).'){'.$match_times.'}';  # s(?:d|x){2}
                }
            }while(--$match_times>0);

            $match_items[] = $end_item;
            if($first_item)array_unshift($match_items,$first_item);
            $rule_str = implode('|',$match_items);
        }
		$args_rule[$key] = '/(?:\-+)(?:'.$rule_str.')((?:(?! -).+)?) -/U';
    }

    #print_r($args_rule);

    $stdin = array(STDIN); $w = $e = null;
    $has_stdin = (bool)stream_select($stdin,$w,$e,0);
    $stdin_str = $has_stdin ? file_get_contents('php://stdin') : null;

	$params = array();
	foreach($args_rule as $k=>$s){
		if( preg_match_all($s,$args,$found) ){
            if(isset($use_stdin[$k]) && $stdin_str){
                $params[$k] = $stdin_str;
                continue;
            }
            if(isset($is_booleans[$k])){
                $params[$k] = true;
                continue;
            }
			if('all' != $end_first_all){
				$fun = ($end_first_all=='first') ? 'current' : 'end';
				$value = $fun($found[1]);
				if(substr($value,0,1) == '=') $value = substr($value,1);
				if(empty($value)) $value = true;
				$params[$k] = trim($value);
			}else{
				foreach($found[1] as $item){
					if(substr($item,0,1) == '=') $item = substr($item,1);
					if(empty($item)) $item = true;
					$params[$k][] = trim($item);
				}
			}
        }else{
            if(isset($is_booleans[$k])){
                $params[$k] = false;
                continue;
            }
        }
	}
	return $params;
}

/*
format: \033[颜色代码;颜色代码;颜色代码m

颜色表  
前景             背景              颜色
   ---------------------------------------
   30                40               黑色
   31                41               紅色
   32                42               綠色
   33                43               黃色
   34                44               藍色
   35                45               紫紅色
   36                46               青藍色
   37                47               白色

代码              意义
   -------------------------
   0                 OFF
   1                 高亮显示
   4                 underline
   5                 闪烁
   7                 反白显示
   8                 不可见 
*/
function getColorText($text,$foreground=32,$background=1){
    $after  = "\033[0m";
    $before = "\033[$foreground;{$background}m";
    return $before.$text.$after;
}

function showhelp(array $rules,array $description){
	$s = "Usage: ".basename($_SERVER['argv'][0])." [".getColorText('OPTIONS',31)."]\n\n".getColorText('OPTIONS',31).":\n";
    $use_stdin = $booleans = array(); 

	foreach($rules as $key=>$rule){
        $rule_items = explode(';',strtolower($rule));
        if(($index = array_search('stdin',$rule_items))!==false){
            unset($rule_items[$index]); 
            $use_stdin[$key] = true;
        }
        if(($index = array_search('boolean',$rule_items))!==false){
            unset($rule_items[$index]); 
            $booleans[$key] = true;
        }
        $rule = implode(';',$rule_items);
		$args_rule[$key] = '-('.getColorText($rule,'32;1;4').')';
    }

    $or = getColorText(' | ',33);
    foreach($args_rule as $name => $item){
        $space = '    ';
        if(isset($booleans[$name])){
            $s .= $space.$item;
        }else{
            $s .= $space.$item.getColorText($name,31).$or.$item.'='.getColorText($name,31).$or.$item.' '.getColorText($name,31);
        }
        if(isset($use_stdin[$name])){
            $s .= $or.$item.' < '.getColorText('$stdin',31);
        }
        $s .= "\n".'        '.$description[$name]."\n\n";
    }
    return $s;
}
